package com.zorn.template;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.io.Writer;
import java.sql.Connection;
import java.sql.DatabaseMetaData;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.log4j.Logger;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;

import freemarker.template.Configuration;
import freemarker.template.DefaultObjectWrapper;
import freemarker.template.TemplateException;

public class Engine {

	private static Logger log = Logger.getLogger(Engine.class);

	private static final String TRUE = "true", FALSE = "false", DEFAULT_CONFIG_XML_PATH = "zorn-template.xml", DEFAULT_ENCODING = "UTF-8";

	private static final char DOT = '.', SLASH = System.getProperty("file.separator").charAt(0), I = 'I';

	private static final File TEMPLATE_DIR = new File(Engine.class.getClassLoader().getResource("").getPath() + "/com/zorn/template/template"), SOURCE_DIR = new File(System.getProperty("user.dir") + "/src/main/java");
	private static final String[] TABLE_TYPES = null;

	private Connection conn;

	private DatabaseMetaData meta;

	private Configuration freemarker;

	private List<Map<String, Object>> packages, datas;

	private Map<String, String> properties;

	//	private List<String> templates;

	public void getJDBCProperties() {

	}

	public Connection getConnection() {
		if (conn == null) {
			try {
				Class.forName("com.mysql.jdbc.Driver");
				conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/mis3", "root", "root");
			} catch (ClassNotFoundException e) {
				e.printStackTrace();
			} catch (SQLException e) {
				e.printStackTrace();
			}
		}
		return conn;
	}

	public DatabaseMetaData getDatabaseMetaData() {
		if (meta == null) {
			try {
				meta = getConnection().getMetaData();
			} catch (SQLException e) {
				e.printStackTrace();
			}
		}
		return meta;
	}

	public void parseConfigXml() {
		properties = new HashMap<String, String>();
		properties.put("overwrite", "true");
		properties.put("catalog", "platform");
		properties.put("target", "com.zorn.platform");
		properties.put("source_dir", "src/main/java");

		/*packages = new ArrayList<Map<String, Object>>();
		Map<String, Object> p = new HashMap<String, Object>();
		p.put("name", "zorn-platform-core");
		p.put("package", "com.zorn.platform");
		p.put("database", "platform");
		p.put("autoscan", "true");
		p.put("overwrite", "false");
		packages.add(p);*/

		Map<String, Object> p2 = new HashMap<String, Object>();
		p2.put("name", "zorn-platform-extra");
		p2.put("target", "com.zorn.test");
		p2.put("database", "platform");
		p2.put("overwrite", "true");
		p2.put("autoscan", "true");
		//		List<Map<String, String>> modules = new ArrayList<Map<String, String>>();
		//		Map<String, String> feedback = new HashMap<String, String>();
		//		feedback.put("name", "component");
		//		feedback.put("table", "platform_component");
		//		modules.add(feedback);
		//		p2.put("modules", modules);
		packages = new ArrayList<Map<String, Object>>();
		packages.add(p2);
	}

	public void initTemplateEngine() {
		freemarker = new Configuration();
		try {
			freemarker.setDirectoryForTemplateLoading(TEMPLATE_DIR);
			freemarker.setObjectWrapper(new DefaultObjectWrapper());
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

	public void initData() throws SQLException, TemplateException, IOException {
		datas = new ArrayList<Map<String, Object>>();
		Map<String, Object> pkg;

		for (int i = 0, j = packages.size(); i < j; i++) {
			pkg = packages.get(i);
			if (pkg.containsKey("modules")) {
				parsePackage(pkg);
			} else if (!FALSE.equalsIgnoreCase(pkg.get("autoscan").toString()) && TRUE.equalsIgnoreCase(pkg.get("overwrite").toString())) {
				parsePackage(scanModules(pkg));
			} else {
				// PRINT DEBUG
				continue;
			}
		}

		//		build(SOURCE_DIR + "/com/zorn/platform" + "/component/mapper/Component.xml", "mapper/mappers.ftl", datas.get(0));
	}

	@SuppressWarnings({ "unchecked", "unused" })
	private void buildAll() throws TemplateException, IOException {

		for (int i = 0; i < datas.size(); i++) {
			Map<String, Object> data, mapper, dao, service, impl, action, controller;
			data = datas.get(i);
			mapper = (Map<String, Object>) data.get("mapper");
			dao = (Map<String, Object>) data.get("dao");
			service = (Map<String, Object>) ((Map<String, Object>) data.get("service")).get("interface");
			impl = (Map<String, Object>) ((Map<String, Object>) data.get("service")).get("implement");
			action = (Map<String, Object>) data.get("action");
			controller = (Map<String, Object>)data.get("controller");

			build(mapper.get("path").toString(), "mapper/mapper.ftl", data);
			build(dao.get("path").toString(), "dao/interface.ftl", data);
			build(service.get("path").toString(), "service/interface.ftl", data);
			build(impl.get("path").toString(), "service/implement.ftl", data);
			build(controller.get("path").toString(), "controller/controller.ftl", data);
		}
	}

	public String toFilePath(String packagePath) {
		return SLASH + packagePath.replace(DOT, SLASH);
	}

	public void parsePackage(Map<String, Object> pkg) throws SQLException {
		String target = "com.zorn.default", packagex = "undefined", module = "undefined", mapping = "";
		Map<String, Object> mod, tbl, data;

		if (pkg.get("target") != null) {
			target = pkg.get("target").toString();
		} else if (properties.get("target") != null) {
			target = properties.get("target");
		}

		@SuppressWarnings("unchecked")
		List<Map<String, Object>> mods = (List<Map<String, Object>>) pkg.get("modules");

		for (int k = 0, l = mods.size(); k < l; k++) {
			data = new HashMap<String, Object>();
			mod = mods.get(k);

			if (getOverwrite(pkg, mod)) {
				data.put("module", mod);
				ResultSet tblRs = meta.getTables(properties.get("catalog"), null, mod.get("table").toString(), new String[] { "TABLE" });
				if (tblRs.next()) {
					tbl = new HashMap<String, Object>();
					String tblName = tblRs.getString("TABLE_NAME");

					JSONObject tblCmt = JSON.parseObject(getTableComment(tblName));
					if (tblCmt == null) {
						/*tblCmt = new JSONObject(8);
						
						String[] split = ArrayUtils.remove(StringUtils.split(tblName, "_"), 0);
						for (int i = 0; i < split.length; i++) {
							split[i] = StringUtils.capitalize(split[i]);
						}
						
						tblCmt.put("package", StringUtils.join(split).toLowerCase());*/
						continue;
					}

					if (tblCmt.containsKey("module")) {
						module = StringUtils.capitalize(tblCmt.getString("module"));
						String[] split = ArrayUtils.remove(StringUtils.split(tblName, "_"), 0);
						for (int i = 0; i < split.length; i++) {
							mapping += "/" + split[i];
						}
					} else {
						String[] split = ArrayUtils.remove(StringUtils.split(tblName, "_"), 0);
						for (int i = 0; i < split.length; i++) {
							mapping += "/" + split[i];
							split[i] = StringUtils.capitalize(split[i]);
						}
						module = StringUtils.join(split);
					}

					if (tblCmt.containsKey("package")) {
						packagex = tblCmt.getString("package");
					} else {
						packagex = StringUtils.lowerCase(module);
					}

					tblCmt.put("name", tblName);
					tbl.putAll(tblCmt);

					ResultSet pkRs = meta.getPrimaryKeys(properties.get("catalog"), null, tblName);
					int count = 0;
					while (pkRs.next()) {
						if (count == 0) tbl.put("primary", pkRs.getString("COLUMN_NAME"));
						count++;
					}
					tbl.put("primary_count", count);

					List<Map<String, Object>> columns = new ArrayList<Map<String, Object>>();
					ResultSet colRs = meta.getColumns(properties.get("catalog"), null, tblName, null);
					while (colRs.next()) {
						JSONObject colCmt = JSON.parseObject(colRs.getString("REMARKS"));
						if (colCmt == null) colCmt = new JSONObject();
						colCmt.put("name", colRs.getString("COLUMN_NAME"));
						columns.add(colCmt);
					}
					tbl.put("columns", columns);
					data.put("table", tbl);

					/* 组装数据
					================================================================= */
					Map<String, Object> mapper = new HashMap<String, Object>(), dao = new HashMap<String, Object>(), service = new HashMap<String, Object>(), _interface = new HashMap<String, Object>(), impl = new HashMap<String, Object>(), action = new HashMap<String, Object>();

					mapper.put("name", module);
					mapper.put("namespace", target + DOT + packagex + DOT + "mapper" + DOT + mapper.get("name"));
					mapper.put("path", SOURCE_DIR.getPath() + toFilePath(mapper.get("namespace").toString()) + ".xml");
					data.put("mapper", mapper);

					dao.put("name", module + "Dao");
					dao.put("package", target + DOT + packagex + DOT + "dao");
					dao.put("namespace", dao.get("package").toString() + DOT + dao.get("name"));
					dao.put("path", SOURCE_DIR.getPath() + toFilePath(dao.get("namespace").toString()) + ".java");
					data.put("dao", dao);

					_interface.put("name", I + module + "Service");
					_interface.put("package", target + DOT + packagex + DOT + "service");
					_interface.put("namespace", _interface.get("package").toString() + DOT + _interface.get("name"));
					_interface.put("path", SOURCE_DIR.getPath() + toFilePath(_interface.get("namespace").toString()) + ".java");
					service.put("interface", _interface);

					impl.put("name", module + "Service");
					impl.put("package", target + DOT + packagex + DOT + "service.impl");
					impl.put("namespace", impl.get("package").toString() + DOT + impl.get("name"));
					impl.put("path", SOURCE_DIR.getPath() + toFilePath(impl.get("namespace").toString()) + ".java");
					service.put("implement", impl);

					data.put("service", service);

					action.put("name", module + "Action");
					action.put("package", target + DOT + packagex + DOT + "action");
					action.put("namespace", action.get("package").toString() + DOT + action.get("name"));
					action.put("path", SOURCE_DIR.getPath() + toFilePath(action.get("namespace").toString()) + ".java");
					data.put("action", action);

					Map<String, Object> controller = new HashMap<String, Object>();
					controller.put("mapping", mapping);
					mapping = "";
					controller.put("name", module + "Controller");
					controller.put("package", target + DOT + packagex + DOT + "controller");
					controller.put("namespace", controller.get("package").toString() + DOT + controller.get("name"));
					controller.put("path", SOURCE_DIR.getPath() + toFilePath(controller.get("namespace").toString()) + ".java");
					data.put("controller", controller);

					datas.add(data);
				}
			}
		}
	}

	private boolean getOverwrite(Map<String, Object> pkg, Map<String, Object> mod) {
		Object overwrite = false, pack = pkg.get("overwrite"), module = mod.get("overwrite");
		String prop = properties.get("overwrite");

		if (prop != null) overwrite = prop;
		if (pack != null) overwrite = pack;
		if (module != null) overwrite = module;

		return TRUE.equalsIgnoreCase((overwrite == null ? FALSE : overwrite).toString());
	}

	public void parseModule() {

	}

	public void parseTable() {

	}

	public void parseColumn() {

	}

	public Map<String, Object> scanModules(Map<String, Object> pkg) throws SQLException {
		ResultSet tblRs = meta.getTables(properties.get("catalog"), null, null, TABLE_TYPES);
		packages.clear();
		List<Map<String, Object>> mods = new ArrayList<Map<String, Object>>();
		while (tblRs.next()) {
			Map<String, Object> mod = new HashMap<String, Object>();
			mod.put("table", tblRs.getString("TABLE_NAME"));
			mods.add(mod);
		}
		pkg.put("modules", mods);
		return pkg;
	}

	public String getTableComment(String table) throws SQLException {
		ResultSet comment = conn.prepareStatement(String.format("SELECT TABLE_COMMENT FROM information_schema.TABLES WHERE TABLE_NAME = '%s'", table)).executeQuery();
		return comment.next() ? comment.getString("TABLE_COMMENT") : null;
	}

	public void build(String target, String template, Map<String, Object> data) throws TemplateException, IOException {
		build(target, template, DEFAULT_ENCODING, data);
	}

	public void build(String target, String template, String encoding, Map<String, Object> data) throws TemplateException, IOException {
		//		File temp = new File(System.getProperty("user.dir") + "/src/main/java/com/zorn/platform/role/dao/RoleDao.java");

		File temp = new File(target);

		if (temp.exists() && !false) {
			//			continue;
		} else {
			temp.getParentFile().mkdirs();
		}

		build(temp, template, DEFAULT_ENCODING, data);
	}

	public void build(File target, String template, Map<String, Object> data) throws TemplateException, IOException {
		build(target, template, DEFAULT_ENCODING, data);
	}

	public void build(File target, String template, String encoding, Map<String, Object> data) throws TemplateException, IOException {
		FileOutputStream fos = new FileOutputStream(target);

		Writer out = new OutputStreamWriter(fos, encoding);

		freemarker.getTemplate(template, encoding).process(data, out);

		out.flush();
		out.close();
	}

	public void run() throws Exception {
		run(DEFAULT_CONFIG_XML_PATH);
	}

	public void run(String configXmlPath) throws Exception {
		// 解析配置文件
		long start = System.currentTimeMillis();
		log.debug("正在解析配置文件……");
		parseConfigXml();
		log.debug("耗时 : " + (System.currentTimeMillis() - start));
		start = System.currentTimeMillis();
		// 初始化模板引擎
		log.debug("正在初始化模板引擎……");
		initTemplateEngine();
		log.debug("耗时 : " + (System.currentTimeMillis() - start));
		start = System.currentTimeMillis();
		// 读取数据库信息
		log.debug("正在初始化数据库连接……");
		getConnection();
		getDatabaseMetaData();
		conn.setReadOnly(true);
		conn.setCatalog("platform");
		log.debug("耗时 : " + (System.currentTimeMillis() - start));
		start = System.currentTimeMillis();
		// 分析转换数据
		log.debug("正在分析数据……");
		initData();
		log.debug("耗时 : " + (System.currentTimeMillis() - start));
		start = System.currentTimeMillis();

		// 保存文件
		log.debug("正在生成文件……");
		buildAll();
		log.debug("耗时 : " + (System.currentTimeMillis() - start));
		//		build(SOURCE_DIR + "/com/zorn/platform/role/dao/RoleDao.java", "dao/interface.ftl", data);
	}

	public static void main(String[] args) throws Exception {

		new Engine().run();

		//		String start = "1.12", limit = "";
		//		
		//		System.out.println(start.matches("^\\d+$"));

		/*Map<String, Object> p2 = new HashMap<String, Object>();
		p2.put("name", "zorn-platform-extra");
		p2.put("target", "com.zorn.platform");
		p2.put("database", "platform");
		p2.put("overwrite", true);
		p2.put("autoscan", "true");*/

		//		Configuration cfg = new Configuration();

		/*Properties properties = new Properties();
		properties.load(Engineer.class.getResourceAsStream("/hibernate.properties"));
		cfg.setProperties(properties);
		
		System.out.println(cfg.getProperties());*/

		//		StandardPBEStringEncryptor encryptor = new StandardPBEStringEncryptor();
		//		encryptor.setPassword("china");
		//		

		//		System.out.println(String.format("%d条数据更新%s。", 5, "成功"));
		//
		//		String p = "123";
		//
		//		BasicPasswordEncryptor pe = new BasicPasswordEncryptor();
		//		String password = pe.encryptPassword(p);
		//		System.out.println(password);
		//		System.out.println(password.length());
		//		System.out.println(new BasicPasswordEncryptor().checkPassword(p, password));
		//
		//		System.out.println(p);
		//
		//		RFC2307MD5PasswordEncryptor md = new RFC2307MD5PasswordEncryptor();
		//		System.out.println(md.encryptPassword("123"));
		//
		//		Map<String, Comparable> map = new HashMap<String, Comparable>();
		//		map.put("success", true);
		//		map.put("count", 5);
		//		map.put("date", new Date());
		//		
		//
		//		System.out.println(JSON.toJSONStringWithDateFormat(map, JSON.DEFFAULT_DATE_FORMAT));
		//		
		//		
		//		System.out.println("/index.html/#/x.xml/xs.jsx".matches("/index.html/#/*/*.js".replace("*", "[^/]+")));
		//		
		//		System.out.println(JSON.parseObject(null));
		//		
		//		
		//		
		//		List<Integer> a = new ArrayList<Integer>(); 
		//		a.add(1);
		//		a.add(2);
		//		a.add(3);
		//		
		//		List<Integer> b = new ArrayList<Integer>(); 
		//		b.add(1);
		//		b.add(2);
		//		b.add(3);
		//		
		//		System.out.println(ListUtils.union(a, b));
		//		
		//		System.out.println(Boolean.parseBoolean(null));
		//		
		//		
		//		System.exit(1);

		//		String json = "{\"success\":true,\"count\":252,\"date\":\"2012-5-20 09:50:00\"}";
		//		
		//		BasicTextEncryptor text = new BasicTextEncryptor();
		//		text.setPassword("china");
		//		String te = text.encrypt(json);
		//		System.out.println(te);
		//		System.out.println(te.length());
		//		
		//		String td = text.decrypt(te);
		//		System.out.println(td);

	}

}
